/********************************************************************************
    @文件名：         strproc.c
    @作者：           矛盾聚合体
    @版本：           1.0
    @日期：           2019-2-28
    @描述：           字符处理函数
    @历史：
********************************************************************************/
#include "strproc.h"

/********************************************************************************
    @函数名：       strgsr
    @函数描述：     取文本分隔符右边
    @函数作者：     矛盾聚合体
    @输入参数：
                    参数名    参数类型  参数描述
                    @dst：    uint8_t*        结果字符串
                    @src：    uint8_t*        源字符串
                    @separator：uint8_t         分隔符号
    @返回值：       uint8_t 0为未寻找到分隔符号，1为找到
    @其他：         可以自身为源和目标
********************************************************************************/
char strgsr(char *dst, char *src, char separator)
{
    char *src_copy = (char *)malloc(strlen(src) + 1);
    strcpy(src_copy, src);
    if (!strchr(src_copy, separator)) //未找到分隔符
    {
        dst[0] = '\0';
        free(src_copy);
        return 0;
    }
    strcpy(dst, strchr(src_copy, separator) + 1);
    free(src_copy);
    return 1;
}

/********************************************************************************
    @函数名：       strgsl
    @函数描述：     取文本分隔符左边
    @函数作者：     矛盾聚合体
    @输入参数：
                    参数名    参数类型  参数描述
                    @dst：    uint8_t*       结果字符串
                    @src：    uint8_t*       源字符串
                    @separator：uint8_t      分隔符号
    @返回值：       uint8_t 0为未寻找到分隔符号，1为找到
    @其他：         可以自身为源和目标
********************************************************************************/
char strgsl(char *dst, char *src, char separator)
{
    char *src_copy = (char *)malloc(strlen(src) + 1);
    strcpy(src_copy, src);
    if (!strchr(src_copy, separator)) //未找到分隔符
    {
        memcpy(dst, src_copy, strlen(src) + 1);
        free(src_copy);
        return 0;
    }
    memcpy(dst, src_copy, strchr(src_copy, separator) - src_copy);
    *(dst + (strchr(src_copy, separator) - src_copy)) = '\0';
    free(src_copy);
    return 1;
}
/********************************************************************************
    @函数名：       strrgsr
    @函数描述：     从右边开始搜索取文本分隔符右边
    @函数作者：     矛盾聚合体
    @输入参数：
                    参数名    参数类型  参数描述
                    @dst：    char*     结果字符串
                    @src：    char*     源字符串
                    @separator：char    分隔符号
    @返回值：       char 0为未寻找到分隔符号，1为找到
    @其他：         可以自身为源和目标
********************************************************************************/
char strrgsr(char *dst, char *src, char separator)
{
    char *src_copy = (char *)malloc(strlen(src) + 1);
    strcpy(src_copy, src);
    if (!strchr(src_copy, separator)) //未找到分隔符
    {
        memcpy(dst, src_copy, strlen(src) + 1);
        free(src_copy);
        return 0;
    }
    strcpy(dst, strrchr(src_copy, separator) + 1);
    free(src_copy);
    return 1;
}
/********************************************************************************
    @函数名：       strrgsl
    @函数描述：     从右边开始搜索取文本分隔符左边
    @函数作者：     矛盾聚合体
    @输入参数：
                    参数名    参数类型  参数描述
                    @dst：    char*     结果字符串
                    @src：    char*     源字符串
                    @separator：char    分隔符号
    @返回值：       char
    @其他：         可以自身为源和目标
********************************************************************************/
char strrgsl(char *dst, char *src, char separator)
{
    char *src_copy = (char *)malloc(strlen(src) + 1);
    strcpy(src_copy, src);
    if (!strchr(src_copy, separator)) //未找到分隔符
    {
        dst[0] = '\0';
        free(src_copy);
        return 0;
    }
    memcpy(dst, src_copy, strrchr(src_copy, separator) - src_copy);
    *(dst + (strrchr(src_copy, separator) - src_copy)) = '\0';
    free(src_copy);
    return 1;
}
/********************************************************************************
    @函数名：       strupr
    @函数描述：     字母变为大写，其他字符不变
    @函数作者：     矛盾聚合体
    @输入参数：
                    参数名    参数类型  参数描述
                    @dst：    char*     结果字符串
                    @src：    char*     源字符串
    @返回值：       char
    @其他：         可以自身为源和目标
********************************************************************************/
char _strupr(char *dst, char *src)
{
    char *src_copy = (char *)malloc(strlen(src) + 1);
    char *p_src = src_copy;
    strcpy(src_copy, src);
    for (; *p_src != '\0'; p_src++)
    {
        if (*p_src >= 'a' && *p_src <= 'z')
        {
            *p_src = *p_src - 'a' + 'A';
        }
    }
    strcpy(dst, src_copy);
    free(src_copy);
    return 1;
}
/********************************************************************************
    @函数名：       strlwr
    @函数描述：     字母变为小写，其他字符不变
    @函数作者：     矛盾聚合体
    @输入参数：
                    参数名    参数类型  参数描述
                    @dst：    char*     结果字符串
                    @src：    char*     源字符串
    @返回值：       char
    @其他：         可以自身为源和目标
********************************************************************************/
char _strlwr(char *dst, char *src)
{
    char *src_copy = (char *)malloc(strlen(src) + 1);
    char *p_src = src_copy;
    strcpy(src_copy, src);
    for (; *p_src != '\0'; p_src++)
    {
        if (*p_src >= 'A' && *p_src <= 'Z')
        {
            *p_src = *p_src - 'A' + 'a';
        }
    }
    strcpy(dst, src_copy);
    free(src_copy);
    return 1;
}
/********************************************************************************
    @函数名：       strdet
    @函数描述：     删除源字符串中的指定字符
    @函数作者：     矛盾聚合体
    @输入参数：
                    参数名    参数类型  参数描述
                    @dst：    char*     结果字符串
                    @src：    char*     源字符串
                    @ch：     char      要删除的字符
    @返回值：       char
    @其他：         可以自身为源和目标
********************************************************************************/
char strdet(char *dst, char *src, char ch)
{
    char *src_copy = (char *)malloc(strlen(src) + 1);
    char *p_src = src_copy;
    strcpy(src_copy, src);
    dst[0] = '\0';
    for (; *p_src != '\0'; p_src++)
    {
        if (*p_src != ch)
        {
            *dst++ = *p_src;
        }
    }
    *dst++ = '\0';
    free(src_copy);
    return 1;
}
/********************************************************************************
    @函数名：       strmsak
    @函数描述：     删除源字符串中一定范围的字符
    @函数作者：     矛盾聚合体
    @输入参数：
                    参数名    参数类型  参数描述
                    @dst：    char*     结果字符串
                    @src：    char*     源字符串
                    @maskstart：char    屏蔽起始字符
                    @maskend：char      屏蔽结束字符
    @返回值：       char
    @其他：         可以自身为源和目标
********************************************************************************/
char strmsak(char *dst, char *src, char maskstart, char maskend)
{
    char *src_copy = (char *)malloc(strlen(src) + 1);
    char *p_src = src_copy;
    strcpy(src_copy, src);
    dst[0] = '\0';
    for (; *p_src != '\0'; p_src++)
    {
        if (*p_src < maskstart || *p_src > maskend)
        {
            *dst++ = *p_src;
        }
    }
    *dst++ = '\0';
    free(src_copy);
    return 1;
}
/********************************************************************************
    @函数名：       strchrnum
    @函数描述：     搜索源字符串中某字符的个数
    @函数作者：     矛盾聚合体
    @输入参数：
                    参数名    参数类型  参数描述
                    @src：    char*     源字符串
                    @ch：     char      要搜索的字符
    @返回值：       int 搜索到的字符个数
    @其他：         可以自身为源和目标
********************************************************************************/
int strchrnum(char *src, char ch)
{
    char *src_copy = (char *)malloc(strlen(src) + 1);
    char *p_src = src_copy;
    strcpy(src_copy, src);
    int charNum = 0;
    for (; *p_src != '\0'; p_src++)
    {
        if (*p_src == ch)
        {
            charNum++;
        }
    }
    free(src_copy);
    return charNum;
}
/********************************************************************************
    @函数名：       strisjson
    @函数描述：     判断字符串是否是json字符串
    @函数作者：     矛盾聚合体
    @输入参数：
                    参数名    参数类型  参数描述
                    @src：    char*     待判断的字符串
    @返回值：       char 0,不是json 1,是json
    @其他：         可以自身为源和目标
********************************************************************************/
char strisjson(char *src)
{
    char *src_copy = (char *)malloc(strlen(src) + 1);
    strcpy(src_copy, src);
    if (strchr(src_copy, '{') == 0)
    {
        free(src_copy);
        return 0;
    }
    if (strchrnum(src_copy, '{') != strchrnum(src_copy, '}'))
    {
        free(src_copy);
        return 0;
    }
    if (strchrnum(src_copy, '[') != strchrnum(src_copy, ']'))
    {
        free(src_copy);
        return 0;
    }
    if (strchrnum(src_copy, '"') % 2 != 0)
    {
        free(src_copy);
        return 0;
    }
    free(src_copy);
    return 1;
}

// Extracts a floating point value from a string. The following code is based loosely on
// the avr-libc strtod() function by Michael Stumpf and Dmitry Xmelkov and many freely
// available conversion method examples, but has been highly optimized for Grbl. For known
// CNC applications, the typical decimal value is expected to be in the range of E0 to E-4.
// Scientific notation is officially not supported by g-code, and the 'E' character may
// be a g-code word on some CNC systems. So, 'E' notation will not be recognized.
// NOTE: Thanks to Radu-Eosif Mihailescu for identifying the issues with using strtod().
#define MAX_INT_DIGITS 10 // Maximum number of digits in int32 (and float)
uint8_t str2float(char *line, int *char_counter, float *float_ptr)
{
    char *ptr = line + *char_counter;
    unsigned char c;
    bool isnegative;
    uint32_t intval = 0;
    int8_t exp = 0;
    uint8_t ndigit = 0;
    bool isdecimal = false;
    float fval;
    // Grab first character and increment pointer. No spaces assumed in line.
    c = *ptr++;
    // Capture initial positive/minus character
    isnegative = false;

    if (c == '-')
    {
        isnegative = true;
        c = *ptr++;
    }
    else if (c == '+')
    {
        c = *ptr++;
    }

    // Extract number into fast integer. Track decimal in terms of exponent value.
    intval = 0;
    exp = 0;
    ndigit = 0;
    isdecimal = false;

    while (1)
    {
        c -= '0';

        if (c <= 9)
        {
            ndigit++;

            if (ndigit <= MAX_INT_DIGITS)
            {
                if (isdecimal)
                {
                    exp--;
                }

                intval = (((intval << 2) + intval) << 1) + c; // intval*10 + c
            }
            else
            {
                if (!(isdecimal))
                {
                    exp++; // Drop overflow digits
                }
            }
        }
        else if (c == (('.' - '0') & 0xff) && !(isdecimal))
        {
            isdecimal = true;
        }
        else
        {
            break;
        }

        c = *ptr++;
    }

    // Return if no digits have been read.
    if (!ndigit)
    {
        return (false);
    };

    // Convert integer into floating point.
    fval = (float)intval;

    // Apply decimal. Should perform no more than two floating point multiplications for the
    // expected range of E0 to E-4.
    if (fval != 0)
    {
        while (exp <= -2)
        {
            fval *= 0.01;
            exp += 2;
        }

        if (exp < 0)
        {
            fval *= 0.1;
        }
        else if (exp > 0)
        {
            do
            {
                fval *= 10.0;
            } while (--exp > 0);
        }
    }

    // Assign floating point value with correct sign.
    if (isnegative)
    {
        *float_ptr = -fval;
    }
    else
    {
        *float_ptr = fval;
    }

    *char_counter = ptr - line - 1; // Set char_counter to next statement
    return (true);
}
